#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <memory>
#include <unordered_map>
#include <cerrno>
#include <cstring>
#include "Socket.hpp"
#include "Log.hpp"
#include "Epoller.hpp"
#include "Nocopy.hpp"
#include "SetNonBlock.hpp"

class Connection;
class TcpServer;

uint32_t EVENTIN = (EPOLLIN | EPOLLET);      // ET模式读事件
uint32_t EVENTOUT = (EPOLLOUT | EPOLLET);    // ET模式写事件
uint32_t EVENTERR = EPOLLERR;                // 错误事件
uint32_t EVENTHUP = EPOLLHUP;                // 挂起事件
uint32_t EVENTEXCEPT = (EPOLLIN | EPOLLOUT); // 异常事件转换为读写事件

using func_t = std::function<void(std::weak_ptr<Connection>)>;

class Connection{
public:
    Connection(const int &fd)
    :_fd(fd)
    {}

    // 设置对应的回调函数
    void SetHandler(func_t recv, func_t send, func_t except){
        _recv_cb = recv;
        _send_cb = send;
        _except_cb = except;
    }

    void SetTcpServer(std::weak_ptr<TcpServer> tcpserver_ptr){
        _tcpserver_ptr = tcpserver_ptr;
    }

    int Fd(){
        return _fd;
    }

    std::string &Inbuffer(){
        return _inbuffer;
    }

    std::string &Outbuffer(){
        return _outbuffer;
    }

    void AddInbuffer(const std::string &info){
        _inbuffer += info;
    }

    void AddOutbuffer(const std::string &info){
        _outbuffer += info;
    }

    ~Connection()
    {}

private:
    int _fd;
    // 每个fd对应的读写缓冲区
    std::string _inbuffer;
    std::string _outbuffer;

public:
    // 每个fd对应的三个回调函数
    func_t _recv_cb;   // 读回调
    func_t _send_cb;   // 写回调
    func_t _except_cb; // 异常回调

    std::weak_ptr<TcpServer> _tcpserver_ptr;  // 回指指针 指向该链接对应的服务器

    std::string _ip;
    uint16_t _port;
};

static const uint16_t defaultport = 28080;
static const int event_num = 128;

class TcpServer : public std::enable_shared_from_this<TcpServer> , public Nocopy{
public:
    TcpServer(func_t OnMessage, const uint16_t port = defaultport)
    :_port(port)
    ,_listensock_ptr(new MySocket())
    ,_epoller_ptr(new Epoller())
    ,_OnMessage(OnMessage)
    {}

    void Init(){
        _listensock_ptr->Socket();
        // 创建的socket默认是阻塞 要设置成非阻塞
        SetNonBlock(_listensock_ptr->Fd());
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();

        // 将socket添加到连接集合中，并且添加到epoller中
        // socket套接字只需绑定对应的读回调即可，读回调就是处理到来的新客户端连接
        AddConnection(_listensock_ptr->Fd(), EVENTIN, 
            std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        mylog(INFO, "listensocket %d create and add connections sucess...", _listensock_ptr->Fd());
    }

private:
    void AddConnection(const int &fd, const uint32_t &events, 
        func_t recv, func_t send, func_t except, 
        const std::string &ip = "0.0.0.0", const uint16_t &port = 0){

        // 1.创建新的链接
        std::shared_ptr<Connection> new_connection_ptr(new Connection(fd));
        new_connection_ptr->SetHandler(recv, send, except);
        // shared_from_this用来在成员函数中返回当前对象的shared_ptr
        new_connection_ptr->SetTcpServer(shared_from_this());
        new_connection_ptr->_ip = ip;
        new_connection_ptr->_port = port;

        // 2.将新的链接加入到连接集合中
        _connections.insert({fd, new_connection_ptr});

        // 3.将该fd以及对应的关心事件加入到epoller中
        _epoller_ptr->EpollerUpDate(EPOLL_CTL_ADD, fd, events);
    }

    bool IsConnectionSafe(const int &fd){
        auto iter = _connections.find(fd);
        if (iter == _connections.end()){
            return false;
        }
        return true;
    }

    void EnableEvent(const int &fd, bool recvenable, bool sendenable){
        uint32_t events = ((recvenable ? EVENTIN : 0) | (sendenable ? EVENTOUT : 0));
        _epoller_ptr->EpollerUpDate(EPOLL_CTL_MOD, fd, events);
    }

    void EventDispatcher(const int &n){
        for (int i = 0; i < n; i++){
            int fd = _revents[i].data.fd;
            uint32_t events = _revents[i].events;
            // 将异常事件转换为读写事件，在读写事件中处理
            if (events & EVENTERR){
                events |= EVENTEXCEPT;
            }
            if (events & EVENTHUP){
                events |= EVENTEXCEPT;
            }

            if ((events & EVENTIN) && (IsConnectionSafe(fd))){
                // 判断当前fd对应连接的读回调函数是否存在 然后再回调
                if (_connections[fd]->_recv_cb){
                    _connections[fd]->_recv_cb(_connections[fd]);
                }
            }
            else if ((events & EVENTOUT) && (IsConnectionSafe(fd))){
                // 判断当前fd对应链接的写回调函数是否存在 然后再回调
                if (_connections[fd]->_send_cb){
                    _connections[fd]->_send_cb(_connections[fd]);
                }
            }
        }
    }

    void Accepter(std::weak_ptr<Connection> conn){
        if (conn.expired()){
            return;
        }
        // 从weak_ptr中获取一个可用的shared_ptr对象connection
        auto connection = conn.lock();
        while(true){
            std::string clientip;
            uint16_t clientport;

            int newfd = _listensock_ptr->Accept(&clientip, &clientport);
            if(newfd >= 0){
                // 1.将普通的链接fd设置成非阻塞
                SetNonBlock(newfd);
                // 2.添加到连接集合和epoller中
                //   普通的fd需要绑定读回调Recver，写回调Sender，以及异常回调Excepter
                AddConnection(newfd, EVENTIN, 
                    std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                    std::bind(&TcpServer::sender, this, std::placeholders::_1),
                    std::bind(&TcpServer::Excepter, this, std::placeholders::_1), clientip, clientport);
                // 3.日志打印消息
                mylog(Debug, "get a new link, clientip: %s, clientport: %d, fd is %d", clientip.c_str(), clientport, newfd);
            }
            else{
                if (errno == EWOULDBLOCK){
                    break;
                }
                else if (errno == EINTR){
                    continue;
                }
                else{
                    break;
                }
            }
        }
    }

    void Recver(std::weak_ptr<Connection> conn){
        if(conn.expired()){
            return;
        }
        auto connection = conn.lock();
        int fd = connection->Fd();

        // 循环读取 直到没有数据可读为止 只负责将所有数据读取到该fd对应的缓冲区即可
        while (true){
            char buffer[1024];
            memset(buffer, 0, sizeof(buffer));
            ssize_t n=recv(fd, buffer, sizeof(buffer)-1, 0);
            if(n > 0){
                buffer[n] = 0;
                connection->AddInbuffer(buffer);
                std::cout << "Server recv a request: " << buffer << std::endl;
            }
            else if(n == 0){
                mylog(INFO, "client quit, me to, fd is %d", fd);
                connection->_except_cb(connection);
                return;
            }
            else{
                if (errno == EWOULDBLOCK){
                    break;
                }
                else if (errno == EINTR){
                    continue;
                }
                else{
                    mylog(Error, "recv error, fd is %d", fd);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }

        // 该fd对应的缓冲区中的数据由上层服务进行处理
        _OnMessage(connection);
    }

public:

    void sender(std::weak_ptr<Connection> conn){
        if(conn.expired()){
            return;
        }
        auto connection = conn.lock();

        // 循环写入
        std::string &outbuffer = connection->Outbuffer();
        while(true){
            ssize_t n = send(connection->Fd(), outbuffer.c_str(), outbuffer.size(), 0);
            if(n > 0){
                std::cout << "Server send a response: " << outbuffer.substr(0, n) << std::endl;
                outbuffer.erase(0, n);
                if(outbuffer.empty()){
                    break;
                }
            }
            else if(n == 0){
                return;
            }
            else{
                if(errno == EWOULDBLOCK){
                    break;
                }
                else if(errno == EINTR){
                    continue;
                }
                else{
                    mylog(Error, "sender error, fd is %d", connection->Fd());
                    connection->_except_cb(connection);
                    return;
                }
            }
        }

        // 如果本次没有写完，就开启写事件关心
        if(!outbuffer.empty()){
            EnableEvent(connection->Fd(), true, true);
        }
        // 如果本次写完，就关闭写事件关心
        else{
            EnableEvent(connection->Fd(), true, false);
        }
    }

private:

    void Excepter(std::weak_ptr<Connection> conn){
        if(conn.expired()){
            return;
        }
        auto connection = conn.lock();
        int fd = connection->Fd();

        mylog(Warning, "except handler fd is %d, clientip: %s, clientport: %d", fd, connection->_ip.c_str(), connection->_port);

        // 1.从epoller中移除该fd以及对应的事件
        _epoller_ptr->EpollerUpDate(EPOLL_CTL_DEL, fd, 0);

        // 2.关闭fd
        mylog(Debug, "close fd is %d", fd);
        close(fd);

        // 3.从链接集合中删除该fd对应的链接
        mylog(Debug, "remove fd is %d from connections...", fd);
        _connections.erase(fd);
    }

public:
    void EventLoop(){
        _quit = false;

        while (!_quit){
            int n = _epoller_ptr->EpollerWait(_revents, event_num);
            if (n > 0){
                mylog(INFO, "There is an event triggered...");
                EventDispatcher(n);
            }
            else if (n == 0){
                mylog(INFO, "epoll wait time out...");
            }
            else{
                mylog(Error, "epoll wait error...");
                _quit = true;
            }
        }

        _quit = true;
    }

    ~TcpServer()
    {}


private:
    uint16_t _port;
    std::shared_ptr<MySocket> _listensock_ptr;  // 监听socket
    std::shared_ptr<Epoller> _epoller_ptr;      // epoller模型

    std::unordered_map<int, std::shared_ptr<Connection>> _connections;  // 链接集合 建立fd和connection之间的关系
    struct epoll_event _revents[event_num];

    bool _quit = true;

    func_t _OnMessage;
};